
/**
 * 动画工具
 */
export class AniTool {

    /**
     * 按钮从下往上出现缓动
     * @param node 缓动节点
     * @param targetY 缓动目标坐标
     * @param easingStr 缓动easing参数，默认：cubicIn
     * @returns Promise(ndPos)
     */
    showBtnUpAni(node: cc.Node, targetY: number, easingStr: string = "cubicIn") {
        return new Promise<cc.Vec2>((resolve) => {
            node.removeComponent(cc.Widget);
            node.y = -(cc.winSize.height * 0.5 + node.height * 0.5 + 30);
            let ndPos = node.getPosition();
            node.active = true;
            let btn = node.getComponent(cc.Button);
            if (btn) {
                btn.interactable = false;
            }
            cc.tween(node).to(0.5, { y: targetY }, { easing: easingStr }).call(() => {
                if (btn) {
                    btn.interactable = true;
                }
                resolve(ndPos);
            }).start();
        });
    }

    /**
     * 按钮从上往下离开缓动
     * @param node 缓动节点
     * @param easingStr 缓动easing参数，默认：cubicOut
     * @returns Promise(ndPos)
     */
    hideBtnDownAni(node: cc.Node, easingStr: string = "cubicOut") {
        return new Promise<cc.Vec2>((resolve) => {
            let btn = node.getComponent(cc.Button);
            let ndPos = node.getPosition();
            if (btn) {
                btn.interactable = false;
            }
            cc.tween(node).to(0.5, { y: -(cc.winSize.height * 0.5 + node.height * 0.5 + 30) }, { easing: easingStr }).call(() => {
                if (btn) {
                    btn.interactable = true;
                }
                node.active = false;
                resolve(ndPos);
            }).start();
        });
    }

    /**
     * 按钮--冒泡样式出现缓动
     * @param node 缓动节点
     * @param time 动画时长
     * @param isDisable 按钮出现后是否不启用
     * @returns Promise<void>
     */
    showBtnBubbleAni(node: cc.Node, time: number = 0.5, isDisable: boolean = false, delay: number = 0) {
        return new Promise<void>((resolve) => {
            node.scale = 0;
            node.active = true;
            let btn = node.getComponent(cc.Button);
            if (btn) {
                btn.interactable = false;
            }
            cc.tween(node).delay(delay).to(time, { scale: 1 }, { easing: "backOut" }).call(() => {
                if (btn) {
                    btn.interactable = !isDisable;
                }
                resolve();
            }).start();
        });
    }

    /**
     * 按钮--冒泡样式消失缓动
     * @param node 缓动节点
     * @param time 动画时长
     * @param isEnable 动画结束节点是否启用
     * @returns Promise<void>
     */
    hideBtnBubbleAni(node: cc.Node, time: number = 0.5, isEnable: boolean = false, delay: number = 0) {
        return new Promise<void>((resolve) => {
            let btn = node.getComponent(cc.Button);
            if (btn) {
                btn.interactable = false;
            }
            cc.tween(node).delay(delay).to(time, { scale: 0 }, { easing: "backIn" }).call(() => {
                node.active = isEnable;
                resolve();
            }).start();
        });
    }

    /**
     * 按钮组--冒泡样式出现缓动
     * @param node 缓动节点
     * @param time 动画时长
     * @param isDisable 按钮出现后是否不启用
     * @returns Promise<void>
     */
    btnsShowBubbleAni(node: cc.Node, time: number = 0.5, isDisable: boolean = false) {
        return new Promise<void>((resolve) => {
            node.active = true;
            node.children.forEach((elem, index) => {
                elem.scale = 0;
                elem.active = true;
                let btn = elem.getComponent(cc.Button);
                if (btn) {
                    btn.interactable = false;
                }
                cc.tween(elem).delay(index * 0.3).to(time, { scale: 1 }, { easing: "backOut" }).call(() => {
                    if (btn) {
                        btn.interactable = !isDisable;
                    }
                    if (index == node.childrenCount - 1) {
                        resolve();
                    }
                }).start();
            });
        });
    }

    /**
     * 按钮组--冒泡样式消失缓动
     * @param node 缓动节点
     * @param time 动画时长
     * @param isEnable 动画结束节点是否启用
     * @returns Promise<void>
     */
    btnsHideBubbleAni(node: cc.Node, time: number = 0.5, isEnable: boolean = false) {
        return new Promise<void>((resolve) => {
            node.children.forEach((elem, index) => {
                let btn = elem.getComponent(cc.Button);
                if (btn) {
                    btn.interactable = false;
                }
                cc.tween(elem).delay(index * 0.3).to(time, { scale: 0 }, { easing: "backIn" }).call(() => {
                    elem.active = isEnable;
                    if (index == node.childrenCount - 1) {
                        node.active = isEnable;
                        resolve();
                    }
                }).start();
            });
        });
    }

    /**
     * 按钮--水平进入
     * @param node 缓动按钮节点
     * @param time 动画时长
     * @param dir 1从右，-1从左,默认：-1
     * @param isDisable 按钮出现后是否不启用
     * @returns Promise<void>
     */
    btnHorizontalComeIn(node: cc.Node, time: number = 0.5, dir: number = -1, isDisable: boolean = false) {
        return new Promise<void>((resolve) => {
            node.active = true;
            let button = node.getComponent(cc.Button);
            button && (button.interactable = false);
            let pos = node.getPosition();
            node.x = (cc.winSize.width + node.width) * dir;
            cc.tween(node).to(time, { x: pos.x }, { easing: cc.easing.backOut }).call(() => {
                button && (button.interactable = !isDisable);
                resolve();
            }).start();
        });
    }

    /**
     * 按钮--水平出去
     * @param node 缓动按钮节点
     * @param time 动画时长
     * @param dir 1去右，-1去左,默认：-1
     * @param isEnable 动画结束节点是否启用
     * @returns Promise<void>
     */
    btnHorizontalGetOut(node: cc.Node, time: number = 0.5, dir: number = -1, isEnable: boolean = false) {
        return new Promise<void>((resolve) => {
            let button = node.getComponent(cc.Button);
            button && (button.interactable = false);
            cc.tween(node).to(time, { x: (cc.winSize.width + node.width) * dir }, { easing: cc.easing.backIn }).call(() => {
                node.active = isEnable;
                resolve();
            }).start();
        });
    }

    /**
     * 按钮组--水平进入
     * @param nodeLayout 缓动按钮的父节点
     * @param time 动画时长
     * @param dir 1从右，-1从左,默认：-1
     * @param isDisable 按钮出现后是否不启用
     * @returns Promise<void>
     */
    btnsHorizontalComeIn(nodeLayout: cc.Node, time: number = 0.5, dir: number = -1, isDisable: boolean = false) {
        return new Promise<void>((resolve) => {
            nodeLayout.active = true;
            let layout = nodeLayout.getComponent(cc.Layout);
            if (layout) { layout.enabled = false; }

            nodeLayout.children.forEach((node, index) => {
                let button = node.getComponent(cc.Button);
                button && (button.interactable = false);
                node.active = true;
                let pos = node.getPosition();
                node.x = (cc.winSize.width + node.width) * dir;
                cc.tween(node).delay(index * 0.2).to(time, { x: pos.x }, { easing: cc.easing.backOut }).call(() => {
                    button && (button.interactable = !isDisable);
                    if (index == nodeLayout.childrenCount - 1) {
                        if (layout) { layout.enabled = true; }
                        resolve();
                    }
                }).start();
            });
        });
    }

    /**
     * 按钮组--水平出去
     * @param nodeLayout 缓动按钮的父节点
     * @param time 动画时长
     * @param dir 1去右，-1去左,默认：-1
     * @param isEnable 动画结束节点是否启用
     * @returns Promise<void>
     */
    btnsHorizontalGetOut(nodeLayout: cc.Node, time: number = 0.5, dir: number = -1, isEnable: boolean = false) {
        return new Promise<void>((resolve) => {
            let layout = nodeLayout.getComponent(cc.Layout);
            if (layout) { layout.enabled = false; }

            nodeLayout.children.forEach((node, index) => {
                let button = node.getComponent(cc.Button);
                button && (button.interactable = false);
                cc.tween(node).delay(index * 0.2).to(time, { x: (cc.winSize.width + node.width) * dir }, { easing: cc.easing.backIn }).call(() => {
                    if (index == nodeLayout.childrenCount - 1) {
                        nodeLayout.active = isEnable;
                        if (layout) { layout.enabled = true; }
                        resolve();
                    }
                }).start();
            });
        });
    }

    /**
     * 按钮组--竖直进入
     * @param nodeLayout 缓动按钮的父节点
     * @param time 动画时长
     * @param dir 1从上，-1从下,默认：-1
     * @param isDisable 按钮出现后是否不启用
     * @returns Promise<void>
     */
    btnsVerticalComeIn(nodeLayout: cc.Node, time: number = 0.5, dir: number = -1, isDisable: boolean = false) {
        return new Promise<void>((resolve) => {
            nodeLayout.active = true;
            let layout = nodeLayout.getComponent(cc.Layout);
            if (layout) { layout.enabled = false; }

            nodeLayout.children.forEach((elem, index) => {
                let button = elem.getComponent(cc.Button);
                button && (button.interactable = false);
                let pos = elem.getPosition();
                elem.y = (cc.winSize.height + elem.height) * dir;
                cc.tween(elem).delay(index * 0.2).to(time, { y: pos.y }, { easing: cc.easing.backOut }).call(() => {
                    button && (button.interactable = !isDisable);
                    if (index == nodeLayout.childrenCount - 1) {
                        if (layout) { layout.enabled = true; }
                        resolve();
                    }
                }).start();
            });
        });
    }

    /**
     * 按钮组--竖直出去
     * @param nodeLayout 缓动按钮的父节点
     * @param time 动画时长
     * @param dir 1去上，-1去下
     * @param isEnable 动画结束节点是否启用
     * @returns Promise<void>
     */
    btnsVerticalGetOut(nodeLayout: cc.Node, time: number = 0.5, dir: number = -1, isEnable: boolean = false) {
        return new Promise<void>((resolve) => {
            let layout = nodeLayout.getComponent(cc.Layout);
            if (layout) { layout.enabled = false; }

            nodeLayout.children.forEach((elem, index) => {
                let button = elem.getComponent(cc.Button);
                button && (button.interactable = false);
                cc.tween(elem).delay(index * 0.2).to(time, { y: (cc.winSize.height + elem.height) * dir }, { easing: cc.easing.backIn }).call(() => {
                    if (index == nodeLayout.childrenCount - 1) {
                        nodeLayout.active = isEnable;
                        if (layout) { layout.enabled = true; }
                        resolve();
                    }
                }).start();
            });
        });
    }

    /**
     * 四周晃动
     * @param node 晃动节点
     * @param time 晃动时间间隔，默认：0.05
     * @param distance 晃动幅度，默认：20
     * @returns Promise<void>
     */
    allAroundWaggle(node: cc.Node, time: number = 0.05, distance: number = 20) {
        return new Promise<void>((resolve) => {
            cc.tween(node).sequence(
                cc.tween(node).to(time, { y: node.y + distance }).to(time, { x: node.x + distance, y: node.y - distance }).to(time, { x: node.x - distance, y: node.y - distance }).to(time, { x: node.x - distance, y: node.y + distance }),
                cc.tween(node).to(time, { y: node.y + distance, x: node.x + distance }).to(time, { x: node.x + distance, y: node.y - distance }).to(time, { x: node.x - distance, y: node.y - distance }).to(time, { x: node.x - distance, y: node.y + distance }),
                cc.tween(node).to(time, { y: node.y + distance, x: node.x + distance }).to(time, { x: node.x + distance, y: node.y - distance }).to(time, { x: node.x - distance, y: node.y - distance }).to(time, { x: node.x - distance, y: node.y + distance }),
                cc.tween(node).to(time, { y: node.y + distance, x: node.x + distance }).to(time, { y: node.y - distance }),
            ).call(() => {
                resolve();
            }).start();
        });
    }

    /**
     * 显示隐藏对话框动画
     * @param node 目标节点
     * @param isShow 显示为真 隐藏为假
     * @returns Promise
     */
    showHideDialogAnimation(node: cc.Node, isShow: boolean) {
        return new Promise((resolve, reason) => {
            if (node && node.isValid) {
                const SCALE_TIME = 0.26;
                if (isShow) {
                    node.scale = 0;
                    cc.tween(node)
                        .parallel(
                            cc.tween(node).to(SCALE_TIME, { scale: 1 }),
                            cc.tween(node)
                                .by(SCALE_TIME * 0.45, { y: 250 }, { easing: 'cubicOut' })
                                .by(SCALE_TIME * 0.55, { y: -250 }, { easing: 'cubicIn' })
                        )
                        .call(() => {
                            resolve(null);
                        })
                        .start();
                } else {
                    cc.tween(node)
                        .parallel(
                            cc.tween(node).to(SCALE_TIME, { scale: 0 }),
                            cc.tween(node)
                                .by(SCALE_TIME * 0.55, { y: 250 }, { easing: 'cubicOut' })
                                .by(SCALE_TIME * 0.45, { y: -250 }, { easing: 'cubicIn' })
                        )
                        .call(() => {
                            resolve(null);
                        })
                        .start();
                }
            } else {
                reason();
            }
        });
    }
}